package com.libgdxbar.pvzlikegame.asset;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.ObjectMap;
import com.libgdxbar.pvzlikegame.entities.BaseEntity;
import org.libgdxbar.projects.interfaces.asset.AssetCallback;

import java.util.Objects;

public class AssetsControl extends BaseEntity implements Disposable {

    public static final String TAG = AssetsControl.class.getSimpleName();

    @SuppressWarnings("GDXJavaStaticResource")
    private static AssetsControl instance;

    private final ObjectMap<Class<?>, AssetsLoader> loaders = new ObjectMap<>();

    private final AssetManager assetManager = new AssetManager();

    // 执行队列
    private final Array<AssetExecution> executions = new Array<>();
    // 运行中队列
    private final Array<AssetExecution> runQueue = new Array<>();
    // 待删除队列
    private final Array<AssetExecution> deleteQueue = new Array<>();

    AssetsControl() {
        if (Objects.nonNull(instance)) {
            throw new GdxRuntimeException("AssetsControl is already initialized");
        }
        instance = this;
    }

    public static synchronized AssetsControl getInstance() {
        if (Objects.isNull(instance)) {
            instance = new AssetsControl();
        }
        return instance;
    }

    /**
     * 检查资源是否已加载
     *
     * @param fileName 资源文件名
     * @return boolean 是否已加载
     */
    public boolean isLoaded(String fileName) {
        return assetManager.isLoaded(fileName);
    }

    /**
     * 获取资源加载器
     *
     * @param tag 标识类
     * @return 资源加载器
     */
    public static AssetsLoader getLoader(Class<?> tag) {
        return getInstance().getAssetsLoader(tag);
    }

    public AssetManager getAssetManager() {
        return assetManager;
    }

    /**
     * 获取资源加载器
     *
     * @param tag 标识类
     * @return 资源加载器
     */
    protected AssetsLoader getAssetsLoader(Class<?> tag) {
        if (loaders.containsKey(tag)) {
            return loaders.get(tag);
        } else {
            AssetsLoader loader = new AssetsLoader(this, tag);
            loaders.put(tag, loader);
            return loader;
        }
    }

    /**
     * 同步加载资源
     *
     * @param fileName 资源文件名
     * @param type     资源类型
     */
    <T> T get(String fileName, Class<T> type) {
        if (!assetManager.isLoaded(fileName, type)) {
            assetManager.load(fileName, type);
            assetManager.finishLoading();
        }
        return assetManager.get(fileName, type);
    }

    /**
     * 异步加载资源
     *
     * @param fileName 资源文件名
     * @param type     资源类型
     * @param callback 加载完成回调
     */
    <T> void get(String fileName, Class<T> type, AssetCallback<T> callback) {
        if (!assetManager.isLoaded(fileName, type)) {
            assetManager.load(fileName, type);
            if (Objects.nonNull(callback)) {
                executions.add(new AssetExecution(fileName, type, callback));
            }
        } else if (Objects.nonNull(callback)) {
            T asset = assetManager.get(fileName, type);
            callback.finish(asset);
        }
    }

    @Override
    @SuppressWarnings({"rawtypes", "unchecked"})
    public void run(float delta) {

        assetManager.update();

        if (executions.isEmpty()) {
            return;
        }

        deleteQueue.clear();
        runQueue.clear();
        runQueue.addAll(executions);

        for (AssetExecution execution : runQueue) {
            Object asset = assetManager.get(execution.getFileName(), execution.getType(), false);
            AssetCallback callback = execution.getCallback();
            if (Objects.nonNull(asset)) {
                callback.finish(asset);
                deleteQueue.add(execution);
            }
        }
        executions.removeAll(deleteQueue, true);
    }

    /**
     * 通过文件名卸载指定资源
     *
     * @param fileName 资源文件名
     * @return boolean 是否卸载成功
     */
    boolean unload(AssetsLoader loader, String fileName) {
        if (assetManager.isLoaded(fileName)) {
            // 检查是否有其他加载器加载
            boolean otherLoaded = false;
            for (ObjectMap.Entry<Class<?>, AssetsLoader> loadedEntry : loaders) {
                AssetsLoader value = loadedEntry.value;
                if (value != loader && value.isLoaded(fileName)) {
                    otherLoaded = true;
                    break;
                }
            }
            // 如果没有其他加载器加载，则卸载
            if (!otherLoaded) {
                assetManager.unload(fileName);
            }
            return true;
        }
        return false;
    }

    /**
     * 卸载指定标签类资源
     *
     * @param tagClass 资源标签类
     * @return boolean 是否卸载成功
     */
    boolean unload(Class<?> tagClass) {
        if (Objects.nonNull(tagClass) && loaders.containsKey(tagClass)) {
            loaders.remove(tagClass).dispose();
            return true;
        }
        return false;
    }

    @Override
    public void dispose() {
        if (Objects.nonNull(instance)) {
            assetManager.dispose();
            assetManager.clear();
            for (ObjectMap.Entry<Class<?>, AssetsLoader> loader : loaders) {
                loader.value.getAssets().clear();
            }
            loaders.clear();
            instance = null;
            Gdx.app.log(TAG, "AssetsControl disposed");
        } else {
            Gdx.app.error(TAG, "AssetsControl is not initialized");
        }
    }
}
